package com.wgo.precise.server.export;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.xmlbeans.XmlCalendar;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;

import remato.domain.common.Category;
import remato.domain.common.Project;
import remato.domain.common.PropertyDef;
import remato.domain.common.PropertySet;
import remato.domain.common.PropertyValue;
import remato.domain.common.Requirement;
import remato.domain.common.RequirementSystem;
import remato.domain.common.Stakeholder;

import com.wgo.bpot.common.transport.util.DomainModelTypeNamingConvention;
import com.wgo.bpot.server.persist.PersistService;
import com.wgo.precise.server.preciseData.CategoryType;
import com.wgo.precise.server.preciseData.ProjectType;
import com.wgo.precise.server.preciseData.PropertyDefType;
import com.wgo.precise.server.preciseData.PropertySetType;
import com.wgo.precise.server.preciseData.PropertyValueType;
import com.wgo.precise.server.preciseData.RefType;
import com.wgo.precise.server.preciseData.RequirementSystemDocument;
import com.wgo.precise.server.preciseData.RequirementType;
import com.wgo.precise.server.preciseData.StakeholderType;

public class ExportPrecise {
	private Map<Long,RequirementType> requirements;
	private PersistService ps;
	private boolean isValidate = true;
	
	
	
	public boolean isValidate() {
		return isValidate;
	}

	public void setValidate(boolean isValidate) {
		this.isValidate = isValidate;
	}

	public RequirementSystemDocument readFromPersistence() {
		Class serverClass = DomainModelTypeNamingConvention.loadServerClass(RequirementSystem.class);
		RequirementSystem rs = (RequirementSystem)ps.getInstance(serverClass,new Long(-1));
		return createDocument(rs);
	}
	
	public RequirementSystemDocument createDocument(RequirementSystem requirementSystem){
		requirements = new HashMap<Long,RequirementType>();
		RequirementSystemDocument doc = RequirementSystemDocument.Factory.newInstance();
		if (null != requirementSystem) {
			com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc = doc.addNewRequirementSystem();
			exportStakeholders(requirementSystem.getStakeholders(), rsDoc);			
			exportPropertySets(requirementSystem, rsDoc);
			exportProjects(requirementSystem.getProjectChildren(), rsDoc);
			setupLinkedRequirements(requirementSystem,rsDoc);
		}
		if (isValidate) {
			validateDoc(doc);
		}
		return doc;
	}

	private void exportPropertySets(RequirementSystem requirementSystem, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (PropertySet propertySet : requirementSystem.getPropertySets()) {
			PropertySetType propertySetDoc = rsDoc.addNewPropertySet();
			propertySetDoc.setName(str(propertySet.getName()));
			propertySetDoc.setDescription(str(propertySet.getDescription()));
			if (null != propertySet.getUpdatedDate()) {
				propertySetDoc.setLastUpdatedDate(new XmlCalendar(propertySet.getUpdatedDate()));
			}
			for (PropertyDef propertyDef : propertySet.getPropertyDefs()) {
				PropertyDefType propertyDefDoc = propertySetDoc.addNewPropertyDef();
				propertyDefDoc.setName(str(propertyDef.getName()));
				propertyDefDoc.setDescription(str(propertyDef.getDescription()));
				propertyDefDoc.setDefaultValue(str(propertyDef.getDefaultValue()));
				propertyDefDoc.setTypeClass(propertyDef.getPropertyTypeEnum().name());
			}
		}
	}

	private static String str(String str){
		if (null == str) {
			return "";
		} else {
			return str.trim();
		}
	}
	
	private void exportStakeholders(Set<Stakeholder> stakeholders, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Stakeholder stakeholder : stakeholders) {
			StakeholderType stakeholderDoc = rsDoc.addNewStakeholder();
			stakeholderDoc.setStakeholderId(getStakeholderRef(stakeholder));
			stakeholderDoc.setFirstName(str(stakeholder.getFirstName()));
			stakeholderDoc.setLastName(str(stakeholder.getLastName()));
			stakeholderDoc.setDescription(str(stakeholder.getDescription()));
		}
	}

	private void exportProjects(Set<Project> projects, com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Project project : projects) {
			ProjectType projDoc = rsDoc.addNewProject();
			exportProject(project, projDoc);
		}
	}

	private void exportProjects(Set<Project> projects, ProjectType projParentDoc) {
		for (Project project : projects) {
			ProjectType projDoc = projParentDoc.addNewProjectChild();
			exportProject(project, projDoc);
		}
	}

	private void exportProject(Project project, ProjectType projDoc) {
		projDoc.setName(str(project.getName()));
		projDoc.setDescription(str(project.getDescription()));
		exportProjects(project.getProjectChildren(), projDoc);
		exportCategories(project.getCategoryChildren(), projDoc);
	}

	private void exportCategories(Set<Category> categories, ProjectType catParentDoc) {
		for (Category category : categories) {
			CategoryType catDoc = catParentDoc.addNewCategoryChild();
			exportCategory(category, catDoc);
		}
	}

	private void exportCategories(Set<Category> categories, CategoryType catParentDoc) {
		for (Category category : categories) {
			CategoryType catDoc = catParentDoc.addNewCategoryChild();
			exportCategory(category, catDoc);
		}
	}

	private void exportCategory(Category category, CategoryType catDoc) {
		catDoc.setName(str(category.getName()));
		catDoc.setDescription(str(category.getDescription()));
		for (PropertySet propertySet : category.getPropertySets()) {
			RefType propSetRef = catDoc.addNewPropertySet();
			propSetRef.setRef(getPropSetRef(propertySet));
		}
		for (Requirement req : category.getRequirements()) {
			RequirementType reqDoc = catDoc.addNewRequirement();
			exportRequirement(req, reqDoc);
			requirements.put(req.getBusinessId(),reqDoc);
		}
		exportCategories(category.getCategoryChildren(), catDoc);
	}

	private void exportRequirement(Requirement req, RequirementType reqDoc) {
		reqDoc.setRequirementId(req.getBusinessId());
		reqDoc.setName(str(req.getName()));
		reqDoc.setDescription(str(req.getDescription()));
		for (Stakeholder stakeholder : req.getStakeholders()) {
			RefType ref = reqDoc.addNewStakeholder();
			ref.setRef(getStakeholderRef(stakeholder));
		}
		for (PropertyValue propertyValue : req.getPropertyValues()) {
			PropertyValueType propValDoc = reqDoc.addNewPropertyValue();
			RefType propDef = propValDoc.addNewPropertyDef();
			propDef.setRef(getPropDefRef(propertyValue.getPropertyDef()));
			propValDoc.setValue(str(propertyValue.getValue()));
		}
		RefType assignedTo = reqDoc.addNewAssignedTo();
		assignedTo.setRef(getStakeholderRef(req.getAssignedTo()));
		RefType reporterOrOwner = reqDoc.addNewReporterOrOwner();
		reporterOrOwner.setRef(getStakeholderRef(req.getReporterOrOwner()));
		reqDoc.setPriority("" + req.getPriority());
		reqDoc.setStatus("" + req.getStatus());
		reqDoc.setVersion(req.getVersion());
		reqDoc.setLastUpdatedDate(new XmlCalendar(req.getLastUpdatedDate()));
		reqDoc.setSubmittedDate(new XmlCalendar(req.getSubmittedDate()));
	}

	private String getPropDefRef(PropertyDef propertyDef) {
		return str(propertyDef.getName());
	}

	private String getPropSetRef(PropertySet propertySet) {
		return str(propertySet.getName());
	}

	private String getStakeholderRef(Stakeholder stakeholder) {
		return (null == stakeholder) ? "" : str(stakeholder.getFirstName() + stakeholder.getLastName());
	}

	private void setupLinkedRequirements(RequirementSystem requirementSystem,com.wgo.precise.server.preciseData.RequirementSystemDocument.RequirementSystem rsDoc) {
		for (Project project : requirementSystem.getProjectChildren()) {
			for (Category category : project.getCategoryChildren()) {
				for (Requirement req : category.getRequirements()) {
					RequirementType reqDoc = requirements.get(req.getBusinessId());
					for (Requirement linkedReq : req.getLinkedToRequirements()) {
						RefType ref = reqDoc.addNewLinkedRequirement();
						ref.setRef(""+linkedReq.getBusinessId());
					}
				}
			}
		}
	}

	public void setPersistService(PersistService persistService) {
		this.ps = persistService;
	}

	
	public static boolean validateDoc(XmlObject rsDoc){
		 // Create an XmlOptions instance and set the error listener.
		 XmlOptions validateOptions = new XmlOptions();
		 ArrayList errorList = new ArrayList();
		 validateOptions.setErrorListener(errorList);
		 
		 // Validate the XML.
		 boolean isValid = rsDoc.validate(validateOptions);
		 
		 // If the XML isn't valid, loop through the listener's contents,
		 // printing contained messages.
		 if (!isValid)
		 {
		      for (int i = 0; i < errorList.size(); i++)
		      {
		          XmlError error = (XmlError)errorList.get(i);
		          System.out.println("\n");
		          System.out.println("Message: " + error.getMessage() + "\n");
		          System.out.println("Location of invalid XML: " + 
		              error.getCursorLocation().xmlText() + "\n");
		      }
		 }
		 return isValid;
	}
	
}
